import {
    BroadcastRequest,
    BroadcastResponse,
    BroadcastResponseStatus,
    BroadcastType,
    Controller,
    ControllerNo
} from "./model/ControllerOpt";
import {DataRouter} from "../frame/dataSource/DataRouter";
import {ControllerBroadcast} from "./ControllerBroadcast";
import {AchievementSerializeBody, AchLoadType, SerializeBody, SerializeLoadType} from "./model/SerializeBody";
import {
    AchievementBeneficiaryType,
    AchievementData,
    AchievementType,
    AppearanceType,
    SaveType,
    TalentPoolData,
    UnitAttribute
} from "../frame/dataSource/DataModel";
import {Util} from "../utils/Util";
import {Attrs, CaluType, RewardType, SpecialEffectType, UnitPosition} from "../type/Type";
import {GlobalEnv} from "../utils/GlobalEnv";
import {DataRouterUtil} from "../utils/router/DataRouterUtil";
import Configer from "../frame/apiModel/config/Configer";
import {ConfigKey} from "../constant/ConfigConstant";
import {ThreeChooseOneModel} from "../../gui/code/model/GuiModel";
import MathUtil from "../frame/codeAdapter/MathUtil";
import {Logger} from "../frame/apiModel/config/Logger";
import {UnitJson} from "../frame/apiModel/model/StoreJson";
import {PlayerModel} from "../frame/apiModel/adapterModel/model4/PlayerModel";
import {PlayerDataKey} from "../frame/dataSource/DataSource";
import {CodeUtil} from "../frame/codeAdapter/CodeUtil";
import {UnitModel} from "../frame/apiModel/adapterModel/model3/UnitModel";
import {PlayerFactory} from "../frame/PlayerFactory";
import {encrypted} from "../utils/decrypt";
import * as jsonConfig from "../constant/jsonConfig.json";
import {TimeController} from "./TimeController";
import {FinalKey} from "../constant/FinalConstant";
import {ResourceKey} from "../constant/ResourceConstant";

// 成就盖指不显示在装备栏但却是存在加成的物品（称号？成品装备？）
export class AchievementController implements Controller {
    getControllerNo(): number {
        return ControllerNo.AchievementController;
    }
    private static achievementController: AchievementController = new AchievementController();

    init() {
        ControllerBroadcast.getInstance().startListen(AchievementController.getInstance());
    }
    clearAchievementByPlayer(player: PlayerModel) {
        Logger.toastError(`框架暂时不支持删除成就，请联系作者修改`, player);
        // player.data[PlayerDataKey.achievements] = undefined;
        // player.data[PlayerDataKey.achievementAttr] = undefined;
    }
    loadByJson(json: AchievementSerializeBody, player: PlayerModel) {
        let lockKey: number = 0;
        let archiveLockKey: number = 0;

        let oldLockKey = 0;
        let oldArchiveLockKey = 0;
        const archiveIndex = player.data[PlayerDataKey.archiveIndex];
        const integral = json.integ;
        if (integral && integral >= 0) {
            player.data[PlayerDataKey.integral] = integral;
            player.data[PlayerDataKey.integralCache] = integral;
        }

        const tempLockKey = this.getEncryptedLockKey(integral + '')
        lockKey = lockKey + tempLockKey;
        Logger.toastSave(`作弊锁加密积分: ${tempLockKey}`, player)

        const handBookExp = json.freeExp;
        if (handBookExp && handBookExp >= 0) {
            player.data[PlayerDataKey.handBookExp] = handBookExp;

            const tempLockKey = this.getEncryptedLockKey(handBookExp + '')
            lockKey = lockKey + tempLockKey;
            Logger.toastSave(`作弊锁加密经验: ${lockKey}`, player)
        }

        const talentNum = Configer.getConfig(ConfigKey.talentMaxNum) as number;

        let currentTalentNum = 0;

        this.mapAchievementSerialize(archiveIndex, SerializeLoadType.current_global, (key, index, achId, type, bGlobal) => {
            switch (type) {
                case AchLoadType.lock: {
                    if (bGlobal) {
                        oldLockKey = json[key] as number;
                    } else {
                        oldArchiveLockKey = json[key] as number;
                    }
                    break;
                }
                case AchLoadType.ach: {
                    const value = json[key] as number;
                    if (!CodeUtil.isNullOrZero(value)) {
                        const numValue = MathUtil.toNumber(value);
                        // 天赋数量检测
                        const achData = DataRouter.getAchievementDataByAchievementId(achId);
                        const type = achData?.type;
                        if (type && type === AchievementType.talent) {
                            currentTalentNum = currentTalentNum + 1;
                        }
                        if (currentTalentNum > talentNum) {
                            // Logger.toastError(`数据异常，天赋数量超过上限,自动缩减至天赋上限`, player);
                            return;
                        }

                        AchievementController.getInstance().recordAchievement(player, achId, numValue);
                        if (bGlobal) {
                            lockKey = lockKey + this.getEncryptedLockKey(numValue + '');
                            Logger.toastSave(`解密全局成就 ${achId}: ${lockKey}`, player)
                        } else {
                            archiveLockKey = archiveLockKey + this.getEncryptedLockKey(numValue + '');
                            Logger.toastSave(`解密单档成就 ${achId}: ${archiveLockKey}`, player);
                        }
                    }
                    break;
                }
                case AchLoadType.handBook: {
                    const value = json[key] as number;
                    if (!CodeUtil.isNullOrZero(value)) {
                        const numValue = MathUtil.toNumber(value);
                        AchievementController.getInstance().recordHandBook(player, achId, numValue)
                        if (bGlobal) {
                            lockKey = lockKey + this.getEncryptedLockKey(numValue + '');
                            Logger.toastSave(`解密全局成就 ${achId}: ${lockKey}`, player)
                        } else {
                            archiveLockKey = archiveLockKey + this.getEncryptedLockKey(numValue + '');
                            Logger.toastSave(`解密单档成就 ${achId}: ${archiveLockKey}`, player)
                        }
                    }
                    break;
                }
            }
        })


        // 新计算出来的lockKey与旧版本存储的做对比，如果相同表示没有问题
        Logger.toastSave(`作弊锁加密图鉴与成就: ${lockKey}`, player);

        const useLockKey = Configer.getConfig(ConfigKey.useLockKey);
        if (!CodeUtil.isNullOrZero(useLockKey)) {
            if (!CodeUtil.isNullOrZero(archiveLockKey) && !CodeUtil.isNullOrZero(oldArchiveLockKey)) {
                const loseFun = () => {
                    const str = `${player.name} 存档防作弊校验未通过，如有疑问请加入玩家群咨询作者!`;
                    Logger.toastError(str);
                    player.data[PlayerDataKey.bCheat] = true;
                    GlobalEnv.setBeLose(player, true)
                }
                if (archiveLockKey === oldArchiveLockKey) {
                } else {
                    loseFun();
                    return;
                }
            }

            if (!CodeUtil.isNullOrZero(lockKey) && !CodeUtil.isNullOrZero(oldLockKey)) {
                const loseFun = () => {
                    const str = `${player.name} 全局防作弊校验未通过，如有疑问请加入玩家群咨询作者!`;
                    Logger.toastError(str);
                    player.data[PlayerDataKey.bCheat] = true;
                    GlobalEnv.setBeLose(player, true)
                }
                if (lockKey === oldLockKey) {
                } else {
                    loseFun();
                }
            }
        }
    }

    loadApp(player: PlayerModel, json: AchievementSerializeBody, exceptHeroSkin: boolean = false) {
        this.useAppearance(player, json.ha1?.id, AppearanceType.heroArm1, true);
        this.useAppearance(player, json.ha2?.id, AppearanceType.heroArm2, true);
        this.useAppearance(player, json.bs?.id, AppearanceType.boxSkin, true);
        this.useAppearance(player, json.hw?.id, AppearanceType.heroWing, true);
        if (exceptHeroSkin) {
        } else {
            this.useAppearance(player, json.hs?.id, AppearanceType.heroSkin, true);
        }
        this.useAppearance(player, json.hr?.id, AppearanceType.heroRing, true);
        this.useAppearance(player, json.ht?.id, AppearanceType.heroTitle, true);
    }

    getEncryptedLockKey(lockKey: string): number {
        const keys = jsonConfig.encryptKeys;
        let encryptLockKey = encrypted(keys, lockKey, '', '')

        let strKey = encryptLockKey;
        const maxEncryptedLockKeyLen = Configer.getConfig(ConfigKey.maxEncryptedLockKeyLen);
        const len = strKey ? strKey.length : 0;
        if (len > maxEncryptedLockKeyLen) {
            strKey = strKey.slice(len - maxEncryptedLockKeyLen, len)
        }
        const numKey = MathUtil.toNumber(strKey)
        return numKey;
    }

    static getInstance(): AchievementController {
        return AchievementController.achievementController;
    }

    addTalent(player: PlayerModel, achievementId: string, reward: string) {
        const ret = AchievementController.getInstance().addAchievement(player, achievementId, 1);
        if (ret) {
            const hero = GlobalEnv.getHero(player);
            const request: BroadcastRequest = { broadcastType: BroadcastType.reward, controllerNo: ControllerNo.TaskController, data: { activeUnit: hero, targetUnit: hero,  rewardType: RewardType.bag, rewardId: reward } }
            ControllerBroadcast.getInstance().broadcast(request);
        }
    }

    addAchievement(player: PlayerModel, achievementId: string, addNum: number, bSilent: boolean = false) {
        if (achievementId) {
        } else {
            Logger.toastError(`无法获得一个空的成就`);
            return false;
        }
        const achievementData = DataRouter.getAchievementDataByAchievementId(achievementId);
        if (achievementData) {
        } else {
            Logger.toastError(`获取成就失败 没有对应成就 ${achievementId}`);
            return false;
        }
        const num = this.getAchievementNum(player, achievementId);
        const isPlaying = player?.isNotEndPlaying();
        if (isPlaying) {
        } else {
            return false;
        }
        if (!CodeUtil.isNullOrZero(num)) {
            const achievementMaxNum = achievementData.maxNum;
            if (num >= achievementMaxNum) {
                if (bSilent) {
                } else {
                    const failedTip = achievementData.failed_tip;
                    if (failedTip) {
                        Logger.toastProduct(failedTip, player)
                    }
                }
                return false;
            }
        }

        let success = this.recordAchievement(player, achievementId, addNum);
        if (!bSilent) {
            const success_tip = achievementData.success_tip;
            if (success_tip) {
                Logger.toastProduct(success_tip, player)
            }

            let back = 'xx';

            const resourceId = achievementData.UI_path;
            const resourceData = DataRouter.getResourceDataByResourceId(resourceId);
            if (resourceData?.src) {
                back = resourceData.src;
            }

            // const request: BroadcastRequest = { broadcastType: BroadcastType.showUi, controllerNo: ControllerNo.GuiController, data: { player: player, guiId: GuiId.toastWheelBar, data: { path: back } }};
            // ControllerBroadcast.getInstance().broadcast(request);
            // const descript = achievementData.descript;
            // Logger.toastProduct(descript, player)
        }
        if (success) {
        } else {
            return success
        }
        const type = achievementData.beneficiary;
        // 获取新成就后广播给单位处理器 重新计算属性
        const storeRequest: BroadcastRequest = { broadcastType: BroadcastType.addStoreTask, controllerNo: ControllerNo.StoreController, data: { player, no: ControllerNo.AchievementController } }
        ControllerBroadcast.getInstance().broadcast(storeRequest);
        const request: BroadcastRequest = { broadcastType: BroadcastType.addCaluTaskByPlayer, controllerNo: ControllerNo.UnitController, data: { player: player, type: type } }
        ControllerBroadcast.getInstance().broadcast(request);
        return true;
    }
    insertHandBooks(player: PlayerModel, unitInfos: UnitJson[], bWin: boolean): void {
        if (player?.isValid()) {
        } else {
            return;
        }
        // for (let i = 0; i < unitInfos.length; i++) {
        //     const unitInfo = unitInfos[i];
        //     if (unitInfo && unitInfo.id) {
        //         unitInfo.e = this.getNewAddHandBookExp(unitInfo)
        //         this.insertHandBook(player, unitInfo);
        //     }
        // }
        if (bWin) {
            const difficultData = DataRouterUtil.getDifficultData(GlobalEnv.difficult);
            let freeExp = 0;
            if (difficultData) {
                const addFreeExp = difficultData?.freeExp ? difficultData.freeExp : 0;
                freeExp = freeExp + addFreeExp;
            }
            this.addExp(player, freeExp);
            Logger.toastProduct(`获得了${freeExp}点自由分配的图鉴经验`, player)
            const storeRequest: BroadcastRequest = { broadcastType: BroadcastType.addStoreTask, controllerNo: ControllerNo.StoreController, data: { player, no: ControllerNo.AchievementController } }
            ControllerBroadcast.getInstance().broadcast(storeRequest);
        }
    }
    initHandBooks(player: PlayerModel, unitInfo: UnitJson): void {
        if (player?.isValid()) {
        } else {
            return;
        }
        const id = unitInfo && unitInfo.id;
        if (id) {
            const exp = this.getHandBookNum(player, id)
            if (!CodeUtil.isNullOrZero(exp)) {
            } else {
                this.insertHandBook(player, unitInfo);
                const storeRequest: BroadcastRequest = { broadcastType: BroadcastType.addStoreTask, controllerNo: ControllerNo.StoreController, data: { player, no: ControllerNo.AchievementController } }
                ControllerBroadcast.getInstance().broadcast(storeRequest);
            }
        }
    }
    insertHandBook(player: PlayerModel, unitInfo: UnitJson): boolean {
        if (player?.isValid()) {
        } else {
            return false;
        }
        if (unitInfo && unitInfo.id) {
            const id = unitInfo.id;
            const exp = unitInfo.e;
            let ret = this.recordHandBook(player, id, exp);
            return ret;
        }
    }

    getHandBook(player: PlayerModel) {
        if (player?.isValid()) {
        } else {
            return;
        }
        const handBooks = player.data[PlayerDataKey.handBook];
        return handBooks;
    }

    getNewAddHandBookExp(unitInfo: UnitJson): number {
        let e = unitInfo.e;
        e = e / 1000;
        e = MathUtil.ceil(e);
        return e;
    }

    getChooseTalentPoolData (player: PlayerModel): ThreeChooseOneModel[] {
        if (player?.isValid()) {
        } else {
            return [];
        }
        const datas: TalentPoolData[] = [];
        const hero = GlobalEnv.getHero(player);
        const level = hero.level;
        const talentDatas = DataRouter.getTalentDatas();
        for (let i = 0; i < talentDatas.length; i++) {
            const talentData = talentDatas[i];
            if (!CodeUtil.isNullOrZero(talentData.heroLevel)) {
                if (level >= talentData.heroLevel) {
                } else {
                    continue;
                }
            }
            const achId = talentData.achievementId;
            const achNum = this.getAchievementNum(player, achId);
            if (talentData.talentLimit) {
                if (talentData.talentLimit >= achNum + 1) {
                } else {
                    continue;
                }
            }
            datas.push(talentData);
        }
        const chooseAchNum: number = Configer.getConfig(ConfigKey.chooseOneNum);
        const ret = DataRouterUtil.getDataModelByWeight(player, datas, chooseAchNum) as TalentPoolData[];

        const threeChooseOneModels: ThreeChooseOneModel[] = [];
        for (let i = 0; i < chooseAchNum; i++) {
            const talentData = ret[i];
            const achData = DataRouter.getAchievementDataByAchievementId(talentData.achievementId);
            if (achData) {
                const iconPath = achData?.iconPath;
                const resourceData = DataRouter.getResourceDataByResourceId(iconPath);
                const backgroundImage = resourceData?.src;
                const model: ThreeChooseOneModel = { Art: backgroundImage, descript: achData.descript, id: achData.id, name: achData.name, reward: talentData.reward };
                threeChooseOneModels.push(model);
            } else {
                Logger.toastError(`天赋成就 ${talentData.achievementId} 不存在`)
            }
        }
        return threeChooseOneModels;
    }
    checkHasAchs(player: PlayerModel, achIds: string[], level: number) {
        if (achIds) {
            for (let i = 0; i < achIds.length; i++) {
                const ach = achIds[i];
                const ret = this.checkHasAch(player, ach, level);
                if (ret) {
                } else {
                    return false;
                }
            }
        }
        return true
    }

    checkHasAch(player: PlayerModel, achId: string, level: number) {
        const num = this.getAchievementNum(player, achId);
        if (num && num >= level) {
            return true;
        }
        return false
    }
    removeAchievement(player: PlayerModel, achievementId: string) {
        // Logger.toastError(`框架暂时不支持删除成就，请联系作者修改`, player);
        // return;
        if (player?.isValid()) {
        } else {
            return;
        }
        const achievementData = DataRouter.getAchievementDataByAchievementId(achievementId);
        if (achievementData) {
        } else {
            Logger.toastError(`删除成就时 ${achievementId} 成就不存在`);
            return;
        }
        
        const num = this.getAchievementNum(player, achievementId)

        this.removeAttrByAchievementData(player, achievementData, num);

        const technologyId = achievementData.technology;
        if (!CodeUtil.isNullOrZero(technologyId)) {
            this.addTechnology(player, technologyId, 0);
        } else {
            Logger.toastSys(`成就${achievementId}没有绑定科技`, player)
        }
        this.setAchievementNum(player, achievementId, 0);
        const storeRequest: BroadcastRequest = { broadcastType: BroadcastType.addStoreTask, controllerNo: ControllerNo.StoreController, data: { player, no: ControllerNo.AchievementController } }
        ControllerBroadcast.getInstance().broadcast(storeRequest);
    }
    addTechnology(player: PlayerModel, technologyId: string, level: number) {
        if (player?.isValid()) {
        } else {
            return;
        }
        player.setTech(technologyId, level);
    }
    recordAchievement(player: PlayerModel, achievementId: string, addNum: number) {
        if (player?.isValid()) {
        } else {
            return false;
        }

        const achievementData = DataRouter.getAchievementDataByAchievementId(achievementId);
        const oldLevel = this.getAchievementNum(player, achievementId);
        if (achievementData) {
        } else {
            Logger.toastError(`添加成就时 成就 ${achievementId} 不存在`);
            return false;
        }


        let newLevel = oldLevel + addNum;

        const maxNum = achievementData.maxNum ? achievementData.maxNum : 1;

        let success = true;
        if (newLevel > maxNum) {
            // Logger.toastError(`添加成就时 成就 ${achievementId} 大于成就最大值，添加成就失败`);
            newLevel = maxNum
            success = false;
        }

        this.setAchievementNum(player, achievementId, newLevel);

        if (success) {
            this.insertAttrByAchievementData(player, achievementData, addNum);
        }

        const technologyId = achievementData.technology;
        this.addTechnology(player, technologyId, newLevel);

        return success;
    }
    setAchievementNum(player: PlayerModel, achievementId: string, num: number) {
        if (player?.isValid()) {
        } else {
            return false;
        }
        const achievements = player.data[PlayerDataKey.achievements];
        achievements[achievementId] = num
    }
    recordHandBook(player: PlayerModel, handBookId: string, addNum: number): boolean {
        if (player?.isValid()) {
        } else {
            return false;
        }
        let ret = true;
        const handBookData = DataRouter.getHandBookDataByHandBookId(handBookId);
        if (handBookData) {
            const oldExp = this.getHandBookNum(player, handBookId);
            let newExp = oldExp + addNum;

            const maxExp = DataRouterUtil.getHandBookMaxExp(handBookData)

            if (newExp > maxExp) {
                newExp = maxExp;
                // Logger.toastProduct('图鉴无法继续升级', player)
                ret = false;
            }

            this.setHandBookNum(player, handBookId, newExp);

            return ret;
        }
        Logger.toastError(`添加图鉴时 图鉴 ${handBookId} 不存在`);
        return false;
    }
    setHandBookNum(player: PlayerModel, handBookId: string, num: number) {
        if (player?.isValid()) {
        } else {
            return false;
        }
        const handBooks = player.data[PlayerDataKey.handBook];
        handBooks[handBookId] = num
    }
    getHandBookNum(player: PlayerModel, handBookId: string) {
        if (player?.isValid()) {
        } else {
            return 0;
        }
        let num = 0;
        const handBooks = player.data[PlayerDataKey.handBook];
        const ret = handBooks && handBooks[handBookId];
        if (ret) {
            return ret;
        }
        return num;
    }
    getAchievementNum(player: PlayerModel, achievementId: string) {
        if (player?.isValid()) {
        } else {
            return 0;
        }
        let num = 0;
        if (player?.isValid()) {
            const achievements = player.data[PlayerDataKey.achievements];
            const ret = achievements && achievements[achievementId];
            if (ret) {
                return ret;
            }
        }
        return num;
    }
    checkBeneficiary(unit: UnitModel, achId: string) {
        const achData = DataRouter.getAchievementDataByAchievementId(achId);
        const beneficiarys = achData.beneficiary;
        const beneficiaryArr = CodeUtil.getArrBySplit(beneficiarys);
        const isHero = GlobalEnv.isInHeroGroup(unit);
        const isPetRequest: BroadcastRequest = { broadcastType: BroadcastType.isInPets, controllerNo: ControllerNo.UnitController, data: { unit: unit } }
        const isPetRet = ControllerBroadcast.getInstance().broadcast(isPetRequest);
        const isInPets: boolean = isPetRet.data.isInPets;
        const isSummonsRequest: BroadcastRequest = { broadcastType: BroadcastType.isInSummon, controllerNo: ControllerNo.UnitController, data: { unit: unit } }
        const isSummonsRet = ControllerBroadcast.getInstance().broadcast(isSummonsRequest);
        const isInSummoneds: boolean = isSummonsRet.data.isInSummoneds;
        for (let i = 0; i < beneficiaryArr.length; i++) {
            const beneficiary = DataRouterUtil.getNumByArr(beneficiaryArr, i);
            switch (beneficiary) {
                case AchievementBeneficiaryType.hero: {
                    if (isHero) {
                        return true;
                    }
                    break;
                }
                case AchievementBeneficiaryType.pet: {
                    if (isInPets) {
                        return true;
                    }
                    break;
                }
                case AchievementBeneficiaryType.summon: {
                    if (isInSummoneds) {
                        return true;
                    }
                    break;
                }
            }
        }
        return false;
    }
    caluAchievementAttributes(unit: UnitModel): Attrs {
        const player = PlayerFactory.getInstance().getUnitOwner(unit);
        let unitAttr = new UnitAttribute();
        let attrs: Attrs = { unitAttr, playerAttr: undefined }
        if (player?.isValid()) {
        } else {
            return attrs;
        }
        const isHero = GlobalEnv.isInHeroGroup(unit);
        const isPetRequest: BroadcastRequest = { broadcastType: BroadcastType.isInPets, controllerNo: ControllerNo.UnitController, data: { unit: unit } }
        const isPetRet = ControllerBroadcast.getInstance().broadcast(isPetRequest);
        const isInPets: boolean = isPetRet.data.isInPets;
        const isSummonsRequest: BroadcastRequest = { broadcastType: BroadcastType.isInSummon, controllerNo: ControllerNo.UnitController, data: { unit: unit } }
        const isSummonsRet = ControllerBroadcast.getInstance().broadcast(isSummonsRequest);
        const isInSummoneds: boolean = isSummonsRet.data.isInSummoneds;

        // const achievementAttrs = player.data[PlayerDataKey.achievementAttr];


        if (isHero) {
            let heroAttr = player.data[PlayerDataKey.achievementHeroAttr];
            if (heroAttr) {
            } else {
                heroAttr = { unitAttr: new UnitAttribute(), playerAttr: undefined };
            }
            attrs = Util.sumAttributes(attrs, heroAttr, CaluType.add)
        }

        if (isInPets) {
            let petAttr = player.data[PlayerDataKey.achievementPetAttr];
            if (petAttr) {
            } else {
                petAttr = { unitAttr: new UnitAttribute(), playerAttr: undefined };
            }
            attrs = Util.sumAttributes(attrs, petAttr, CaluType.add)
        }

        if (isInSummoneds) {
            let summonAttr = player.data[PlayerDataKey.achievementSummonAttr];
            if (summonAttr) {
            } else {
                summonAttr = { unitAttr: new UnitAttribute(), playerAttr: undefined };
            }
            attrs = Util.sumAttributes(attrs, summonAttr, CaluType.add)
        }

        const handBookAttr = this.getAttrByHandBook(player, unit);
        attrs = Util.sumAttributes(attrs, handBookAttr, CaluType.add);

        return attrs;
    }

    getAttrByHandBook (player: PlayerModel, unit: UnitModel) {
        let attrs: Attrs = { playerAttr: undefined, unitAttr: new UnitAttribute() };
        const unitStr = DataRouterUtil.getUnitStr(unit);
        const handBooks = this.getHandBook(player);
        const handBookData = DataRouter.getHandBookDataByUnitId(unitStr);
        const handBookId = handBookData && handBookData.id;
        const exp = handBooks && handBookId && handBooks[handBookId];
        if (exp) {
            const { lv } = DataRouterUtil.getLvByHandBookExp(exp, handBookData);
            const handBookAttrs = CodeUtil.getArrBySplit(handBookData.attrId);
            for (let i = 0; i < handBookAttrs.length; i ++) {
                if (i >= lv) {
                    break;
                }
                const handBookAttr = handBookAttrs[i];
                const tempAttr = DataRouterUtil.getAttrByAttrInterface(handBookAttr, 1)
                attrs = Util.sumAttributes(attrs, tempAttr, CaluType.add);
            }
        }
        return attrs;
    }
    removeAttrByAchievementData(player: PlayerModel, achievementData: AchievementData, removeNum: number) {
        if (player?.isValid()) {
        } else {
            return;
        }
        // const achievementId = achievementData.id;
        // const num = this.getAchievementNum(player, achievementId);
        const beneficiarys = achievementData.beneficiary;
        const beneficiaryArr = CodeUtil.getArrBySplit(beneficiarys);
        for (let i = 0; i < beneficiaryArr.length; i++) {
            const beneficiary = DataRouterUtil.getNumByArr(beneficiaryArr, i);
            const attrs = DataRouterUtil.getAttrByAttrInterface(achievementData?.attrId, removeNum);
            switch (beneficiary) {
                case AchievementBeneficiaryType.hero: {
                    player.data[PlayerDataKey.achievementHeroAttr] = Util.sumAttributes(player.data[PlayerDataKey.achievementHeroAttr], attrs, CaluType.sub)
                    break;
                }
                case AchievementBeneficiaryType.pet: {
                    player.data[PlayerDataKey.achievementPetAttr] = Util.sumAttributes(player.data[PlayerDataKey.achievementPetAttr], attrs, CaluType.sub)
                    break;
                }
                case AchievementBeneficiaryType.summon: {
                    player.data[PlayerDataKey.achievementSummonAttr] = Util.sumAttributes(player.data[PlayerDataKey.achievementSummonAttr], attrs, CaluType.sub)
                    break;
                }
            }
        }

    }
    insertAttrByAchievementData(player: PlayerModel, achievementData: AchievementData, addNum: number) {
        if (player?.isValid()) {
        } else {
            return;
        }
        const beneficiarys = achievementData.beneficiary;
        const beneficiaryArr = CodeUtil.getArrBySplit(beneficiarys);
        for (let i = 0; i < beneficiaryArr.length; i++) {
            const beneficiary = DataRouterUtil.getNumByArr(beneficiaryArr, i);
            const attrs = DataRouterUtil.getAttrByAttrInterface(achievementData?.attrId, addNum);
            switch (beneficiary) {
                case AchievementBeneficiaryType.hero: {
                    if (player.data[PlayerDataKey.achievementHeroAttr]) {
                    } else {
                        player.data[PlayerDataKey.achievementHeroAttr] = { unitAttr: new UnitAttribute(), playerAttr: undefined }
                    }
                    player.data[PlayerDataKey.achievementHeroAttr] = Util.sumAttributes(player.data[PlayerDataKey.achievementHeroAttr], attrs, CaluType.add)
                    break;
                }
                case AchievementBeneficiaryType.pet: {
                    if (player.data[PlayerDataKey.achievementPetAttr]) {
                    } else {
                        player.data[PlayerDataKey.achievementPetAttr] = { unitAttr: new UnitAttribute(), playerAttr: undefined }
                    }
                    player.data[PlayerDataKey.achievementPetAttr] = Util.sumAttributes(player.data[PlayerDataKey.achievementPetAttr], attrs, CaluType.add)
                    break;
                }
                case AchievementBeneficiaryType.summon: {
                    if (player.data[PlayerDataKey.achievementSummonAttr]) {
                    } else {
                        player.data[PlayerDataKey.achievementSummonAttr] = { unitAttr: new UnitAttribute(), playerAttr: undefined }
                    }
                    player.data[PlayerDataKey.achievementSummonAttr] = Util.sumAttributes(player.data[PlayerDataKey.achievementSummonAttr], attrs, CaluType.add)
                    break;
                }
            }
        }
        // const attrs = DataRouterUtil.getAttrByAttrInterface(achievementData, num);
        // const achievementAttrs = player.data[PlayerDataKey.achievementAttr];
        // achievementAttrs[achievementId] = attrs;
    }

    getAchievement(player: PlayerModel): {[p: string]: number}{
        if (player?.isValid()) {
        } else {
            return;
        }
        const achievement = player.data[PlayerDataKey.achievements];
        return achievement;
    }
    getAddIntegral(bWin: boolean, player: PlayerModel) {
        if (player?.isValid()) {
        } else {
            return;
        }
        const hurts = GlobalEnv.getPlayerHurts();
        let score = 0;
        if (bWin) {
            const integralBase = Configer.getConfig(ConfigKey.integralBase);
            const difficultData = DataRouterUtil.getDifficultData(GlobalEnv.difficult);
            if (difficultData) {
                const addIntegral = difficultData?.integral ? difficultData.integral : 0;
                score = score + addIntegral;
            }
            const firstId = hurts && hurts[0] && hurts[0].id;
            if (firstId && (firstId === player.playerId)) {
                const extra = integralBase;
                Logger.toastProduct(`输出统计第一，积分奖励+${extra}!`, player)
                score = score + extra;
            }
            Logger.toastProduct(`增加积分:${score}`, player)
        }
        score = MathUtil.ceil(score);
        return score;
    }
    rewardIntegral(player: PlayerModel, bWin: boolean) {
        if (player?.isValid()) {
        } else {
            return;
        }
        const rewardNum = this.getAddIntegral(bWin, player);
        this.addIntegral(player, rewardNum)
    }
    achEndStore(player: PlayerModel, unitInfos: UnitJson[], bWin: boolean) {
        if (player?.isValid()) {
        } else {
            return;
        }
        this.insertHandBooks(player, unitInfos, bWin)
        this.rewardIntegral(player, bWin);
    }
    notify(request: BroadcastRequest): BroadcastResponse {
        const ret: BroadcastResponse = { controllerNo: this.getControllerNo(), data: {}, status: BroadcastResponseStatus.success };
        const type = request.broadcastType;
        const data = request.data;
        switch (type) {
            case BroadcastType.serialize: {
                const player: PlayerModel = data.player;
                const serializeBody: SerializeBody = this.serialize(player);
                ret.data = { serializeBody };
                break;
            }
            case BroadcastType.addAchievement: {
                const player: PlayerModel  = data.player;
                const achievementId = data.achievementId;
                const num = data.num;
                const success = this.addAchievement(player, achievementId, num);
                ret.data = { success };
                break;
            }
            case BroadcastType.caluAchievementAttributes: {
                const unit: UnitModel = data.unit;
                const attribute = this.caluAchievementAttributes(unit);
                ret.data = { attribute };
                break;
            }
            case BroadcastType.removeAchievement: {
                const player: PlayerModel  = data.player;
                const achievementId = data.achievementId;
                this.removeAchievement(player, achievementId);
                break;
            }
            case BroadcastType.getAchByPlayer: {
                const player: PlayerModel = data.player;
                const achs = this.getAchievement(player);
                ret.data = { achs };
                break;
            }
            case BroadcastType.achEndStore: {
                const player: PlayerModel = data.player;
                const unitInfos = data.unitInfos;
                const bWin = data.bWin;
                this.achEndStore(player, unitInfos, bWin);
                break;
            }
            case BroadcastType.initHandBook: {
                const player: PlayerModel = data.player;
                const handBookUnitInfo = data.handBookUnitInfo;
                this.initHandBooks(player, handBookUnitInfo)
                break;
            }
            case BroadcastType.getAchNum: {
                const player: PlayerModel = data.player;
                const achId = data.achId;
                const achNum = this.getAchievementNum(player, achId)
                ret.data = { achNum };
                break;
            }
            case BroadcastType.rewardIntegral: {
                const player: PlayerModel = data.player;
                const num = data.num;
                this.addIntegral(player, num)
                break;
            }
            case BroadcastType.rewardFreeExp: {
                const player: PlayerModel = data.player;
                const num = data.num;
                this.addExp(player, num)
                break;
            }
            case BroadcastType.loadApp: {
                const player: PlayerModel = data.player;
                const achHistory: AchievementSerializeBody = data.achHistory;
                this.loadApp(player, achHistory)
                break;
            }
            default :
                ret.status = BroadcastResponseStatus.failed;
                break;
        }
        return ret;
    }
    getIntegral(player: PlayerModel) {
        const integral = player.data[PlayerDataKey.integral];
        return integral;
    }
    getExp(player: PlayerModel) {
        if (player?.isValid()) {
            const exp = player.data[PlayerDataKey.handBookExp];
            return exp;
        }
        return 0;
    }
    addIntegral(player: PlayerModel, num: number) {
        if (player?.isValid()) {
        } else {
            return;
        }
        const integral = player.data[PlayerDataKey.integral];
        let newIntegral = integral + num;
        const check = this.integralCheat(player, newIntegral);
        if (check) {
        } else {
            return;
        }
        player.data[PlayerDataKey.integral] = newIntegral;
        const storeRequest: BroadcastRequest = { broadcastType: BroadcastType.addStoreTask, controllerNo: ControllerNo.StoreController, data: { player, no: ControllerNo.AchievementController } }
        ControllerBroadcast.getInstance().broadcast(storeRequest);
    }
    addExp(player: PlayerModel, num: number) {
        if (player?.isValid()) {
        } else {
            return;
        }
        const exp = player.data[PlayerDataKey.handBookExp];
        player.data[PlayerDataKey.handBookExp] = exp + num;
        const storeRequest: BroadcastRequest = { broadcastType: BroadcastType.addStoreTask, controllerNo: ControllerNo.StoreController, data: { player, no: ControllerNo.AchievementController } }
        ControllerBroadcast.getInstance().broadcast(storeRequest);
    }
    clearIntegralAch (player: PlayerModel) {
        if (player?.isValid()) {
        } else {
            return;
        }
        const integralMalls = DataRouter.getIntegralMallDatas();
        for (let i = 0; i < integralMalls.length; i++) {
            const integralMall = integralMalls[i];
            const integral = integralMall.integral;
            const id = integralMall.id;
            if (!CodeUtil.isNullOrZero(integral)) {
                this.removeAchievement(player, id)
            }
        }
    }
    getIntegralCache(player: PlayerModel) {
        if (player?.isValid()) {
        } else {
            return;
        }
        const integral = player.data[PlayerDataKey.integralCache];
        if (integral) {
            return integral;
        }
        return 0;
    }
    integralCheat(player: PlayerModel, integral: number) {
        if (player?.isValid()) {
        } else {
            return;
        }
        const maxIntegral: number = Configer.getConfig(ConfigKey.maxIntegral);
        let integralCache = this.getIntegralCache(player);

        const subIntegral = integral - integralCache;

        // Util.loggerInDev(subIntegral)

        if (subIntegral >= maxIntegral) {
            const playerName = player.name;
            Logger.toastError(`玩家${playerName} 疑似积分作弊，给予清空积分惩罚!(30%概率清空全部存档)`);
            player.data[PlayerDataKey.integral] = 0;
            this.clearIntegralAch(player);
            return false;
        }
        return true;
    }
    checkIntegral(player: PlayerModel, num: number) {
        if (player?.isValid()) {
        } else {
            return;
        }
        if (!CodeUtil.isNullOrZero(num)) {
            const integral = this.getIntegral(player);
            if (integral >= num) {
                const check = this.integralCheat(player, integral);
                if (check) {
                    return true;
                } else {
                    return false
                }
            }
            Logger.toastProduct(`积分不足`, player)
            return false;
        }
        return true;
    }
    buyAch(player: PlayerModel, integral: number, achId: string) {
        const success = AchievementController.getInstance().checkIntegral(player, integral)
        if (success) {
            const ret = AchievementController.getInstance().consumeIntegral(player, integral);
            if (ret) {
                this.addAchievement(player, achId, 1);
                return true;
            }
            return false;
        }
    }
    consumeIntegral(player: PlayerModel, num: number) {
        if (player?.isValid()) {
        } else {
            return;
        }
        if (!CodeUtil.isNullOrZero(num)) {
            const integral = this.getIntegral(player);
            player.data[PlayerDataKey.integral] = integral - num;
            const storeRequest: BroadcastRequest = { broadcastType: BroadcastType.addStoreTask, controllerNo: ControllerNo.StoreController, data: { player, no: ControllerNo.AchievementController } }
            ControllerBroadcast.getInstance().broadcast(storeRequest);
            return true;
        }
    }
    consumeExp(player: PlayerModel, num: number, handBookId: string) {
        if (player?.isValid()) {
        } else {
            return;
        }
        let consumNum = num;
        const exp = this.getExp(player);
        if (exp > 0) {
        } else {
            Logger.toastProduct('经验剩余为0', player);
            return;
        }
        if (exp >= num) {
        } else {
            consumNum = exp
        }
        const ret = this.insertHandBook(player, { id: handBookId, e: consumNum });
        if (ret) {
            player.data[PlayerDataKey.handBookExp] = exp - consumNum;
            const storeRequest: BroadcastRequest = { broadcastType: BroadcastType.addStoreTask, controllerNo: ControllerNo.StoreController, data: { player, no: ControllerNo.AchievementController } }
            ControllerBroadcast.getInstance().broadcast(storeRequest);
        }
        return true;
    }

    getPositionByAppearanceType(type: AppearanceType) {
        switch (type) {
            case AppearanceType.heroArm1:
                return UnitPosition.HandRight;
            case AppearanceType.heroArm2:
                return UnitPosition.HandLeft;
            case AppearanceType.heroWing:
                return UnitPosition.chest;
            case AppearanceType.heroRing:
                return UnitPosition.origin;
            case AppearanceType.heroTitle:
                return UnitPosition.overhead;
        }
    }
    useAppearance(player: PlayerModel, appId: string, type: AppearanceType, bLoad: boolean = false) {
        if (player?.isValid()) {
        } else {
            return;
        }
        if (appId) {
        } else {
            return;
        }

        let appRecord = player.data[PlayerDataKey.appearanceRecord];
        let record = appRecord[type];
        if (record) {
            switch (type) {
                case AppearanceType.boxSkin: {
                    const box = GlobalEnv.getBox(player);
                    if (box) {
                        const defaultBoxSkin = Configer.getResource(ResourceKey.defaultBoxSkin);
                        box.setModelPath(defaultBoxSkin, true);
                    }
                    break;
                }
                case AppearanceType.heroSkin: {
                    const hero = GlobalEnv.getHero(player);
                    if (hero) {
                        const unitData = DataRouterUtil.getUnitDataByUnit(hero);
                        const defaultModelPath = unitData?.file;
                        if (!CodeUtil.isNullOrZero(defaultModelPath)) {
                            hero.setModelPath(defaultModelPath, true);
                        }
                    }
                    // Logger.toastProduct('英雄皮肤暂时不支持拆卸');
                    break;
                }
                default: {
                    let currentEffect =  record?.effectModel;
                    if (currentEffect) {
                        TimeController.RemoveEffect(currentEffect);
                    }
                    break;
                }
            }
            record.appId = appId;
            record.effectModel = undefined;
        } else {
            appRecord[type] = { effectModel: undefined, appId: appId };
            record = appRecord[type];
        }

        const appData = DataRouter.getAppearanceDataByAppearanceId(appId);
        if (appData) {
            const scale = appData.self_effects_scale;
            const effectPath = appData.self_effects;
            if (type === AppearanceType.heroSkin) {
                const hero = GlobalEnv.getHero(player);
                hero.setModelPath(effectPath, true);
                Logger.toastProduct('皮肤使用成功！', player);
                const request: BroadcastRequest = { broadcastType: BroadcastType.getAchHistory, controllerNo: ControllerNo.StoreController, data: { player: player } }
                const res = ControllerBroadcast.getInstance().broadcast(request);
                const achHistory: AchievementSerializeBody = res.data.achHistory;
                this.loadApp(player, achHistory, true);
            }
            if (type === AppearanceType.boxSkin) {
                const box = GlobalEnv.getBox(player);
                if (box) {
                    box.setModelPath(effectPath, true);
                    Logger.toastProduct('皮肤使用成功！', player)
                }
            }
            let unit: UnitModel;
            if (type === AppearanceType.heroTitle
                || type === AppearanceType.heroWing
                || type === AppearanceType.heroRing
                || type === AppearanceType.heroArm1
                || type === AppearanceType.heroArm2) {
                unit = GlobalEnv.getHero(player)
            }

            if (unit?.isValid()) {
                const foreverPeriodic = Configer.getFinal(FinalKey.foreverPeriodic) as number;
                const position = this.getPositionByAppearanceType(type);
                const newEffect = TimeController.addSpecialEffectByResourceIdFun(unit, effectPath, foreverPeriodic, undefined, position, SpecialEffectType.unit, scale);
                record.effectModel = newEffect;
            }
            // Logger.toastProduct('装备成功！', player)
        }
        if (bLoad) {
        } else {
            const storeRequest: BroadcastRequest = { broadcastType: BroadcastType.addStoreTask, controllerNo: ControllerNo.StoreController, data: { player, no: ControllerNo.AchievementController } }
            ControllerBroadcast.getInstance().broadcast(storeRequest);
        }
    }
    serialize(player: PlayerModel): AchievementSerializeBody {
        const achievementSerialize = player.data[PlayerDataKey.storeCurrent][ControllerNo.AchievementController];
        const archiveIndex = player.data[PlayerDataKey.archiveIndex];
        const achievements = AchievementController.getInstance().getAchievement(player);
        const handBooks = AchievementController.getInstance().getHandBook(player);


        const integral = this.getIntegral(player);
        const exp = this.getExp(player);
        let lockKey = 0;
        lockKey = lockKey + this.getEncryptedLockKey(integral + '');

        Logger.toastSave(`作弊锁加密积分: ${lockKey}`, player)

        const tempLockKey = this.getEncryptedLockKey(exp + '')
        lockKey = lockKey + tempLockKey;
        Logger.toastSave(`作弊锁加密经验: ${lockKey}`, player);

        achievementSerialize.integ = integral;
        achievementSerialize.freeExp = exp;

        let archiveLock = 0;

        this.mapAchievementSerialize(archiveIndex, SerializeLoadType.current_global, (key, index, achId, type, bGlobal) => {
            switch (type) {
                case AchLoadType.lock: {
                    if (bGlobal) {
                        achievementSerialize[key] = lockKey;
                        Logger.toastSave(`作弊锁加密成就与图鉴: ${lockKey}`, player)
                    } else {
                        achievementSerialize[key] = archiveLock;
                        Logger.toastSave(`作弊锁加密成就与图鉴: ${archiveLock}`, player)
                    }
                    break;
                }
                case AchLoadType.ach: {
                    const value = achievements[achId];
                    if (!CodeUtil.isNull(value)) {
                        achievementSerialize[key] = value;
                        if (bGlobal) {
                            lockKey = lockKey + this.getEncryptedLockKey(value + '');
                            Logger.toastSave(`加密成就 ${achId}: ${lockKey}`, player)
                        } else {
                            archiveLock = archiveLock + this.getEncryptedLockKey(value + '');
                            Logger.toastSave(`加密成就 ${achId}: ${archiveLock}`, player)
                        }
                    }
                    break;
                }
                case AchLoadType.handBook: {
                    const value = handBooks[achId];
                    if (!CodeUtil.isNull(value)) {
                        achievementSerialize[key] = value;
                        if (bGlobal) {
                            lockKey = lockKey + this.getEncryptedLockKey(value + '');
                            Logger.toastSave(`加密成就 ${achId}: ${lockKey}`, player)
                        } else {
                            archiveLock = archiveLock + this.getEncryptedLockKey(value + '');
                            Logger.toastSave(`加密成就 ${achId}: ${archiveLock}`, player)
                        }
                    }
                    break;
                }
            }
        });


        const fun = () => {
            const record = player.data[PlayerDataKey.appearanceRecord];
            const bs = record[AppearanceType.boxSkin]?.appId;
            if (bs) {
                achievementSerialize.bs = { id: bs };
            }
            const ha1 = record[AppearanceType.heroArm1]?.appId;
            if (ha1) {
                achievementSerialize.ha1 = { id: ha1 };
            }
            const ha2 = record[AppearanceType.heroArm2]?.appId;
            if (ha2) {
                achievementSerialize.ha2 = { id: ha2 };
            }
            const hr = record[AppearanceType.heroRing]?.appId;
            if (hr) {
                achievementSerialize.hr = { id: hr };
            }
            const hs = record[AppearanceType.heroSkin]?.appId;
            if (hs) {
                achievementSerialize.hs = { id: hs };
            }
            const ht = record[AppearanceType.heroTitle]?.appId;
            if (ht) {
                achievementSerialize.ht = { id: ht };
            }
            const hw = record[AppearanceType.heroWing]?.appId;
            if (hw) {
                achievementSerialize.hw = { id: hw };
            }
        }
        fun();

        return achievementSerialize;
    }

    mapAchievementSerialize(index: number, bAll: SerializeLoadType, callBack: (key: string, index: number, achId: string, type: AchLoadType, bGlobal: boolean) => void) {
        const achievementDatas = DataRouter.getAchievementDatas();
        const handBookDatas = DataRouter.getHandBookDatas();


        const archiveFun = (archiveIndex: number) => {
            for (let i = 0; i < achievementDatas.length; i ++) {
                const achievementData = achievementDatas[i];
                const achievementId = achievementData.id;
                const save = achievementData.save;
                let key;

                switch (save) {
                    case SaveType.archive: {
                        key = `${achievementId}_${archiveIndex}`
                        break;
                    }
                }
                if (!CodeUtil.isNullOrZero(key)) {
                    callBack(key, i, achievementId, AchLoadType.ach, false)
                }
            }

            const lockKey = `lock_${archiveIndex}`
            callBack(lockKey, 0, lockKey, AchLoadType.lock, false)
        }

        const globalFun = () => {
            for (let i = 0; i < achievementDatas.length; i ++) {
                const achievementData = achievementDatas[i];
                const achievementId = achievementData.id;
                const save = achievementData.save;
                let key;
                switch (save) {
                    case SaveType.global: {
                        key = `${achievementId}`
                        break;
                    }
                }
                if (!CodeUtil.isNullOrZero(key)) {
                    callBack(key, i, achievementId, AchLoadType.ach, true)
                }
            }

            for (let i = 0; i < handBookDatas.length; i ++) {
                const handBookData = handBookDatas[i];
                const handBookId = handBookData.id;
                callBack(handBookId, i, handBookId, AchLoadType.handBook, true)
            }

            const appArr = ['bs', 'ha1', 'ha2', 'hr', 'hs', 'ht', 'hw'];
            appArr.forEach(
                (key, index, array) => {
                    callBack(key, index, key, AchLoadType.app, true)
                }
            )

            callBack('integ', 0, 'integ', AchLoadType.other, true)
            callBack('freeExp', 0, 'freeExp', AchLoadType.other, true)

            const lockKey = `lock`
            callBack(lockKey, 0, lockKey, AchLoadType.lock, true)
        }

        switch (bAll) {
            case SerializeLoadType.current_global: {
                globalFun();
            }
            case SerializeLoadType.current_archive: {
                const archiveIndex = index;
                archiveFun(archiveIndex);
                break;
            }
            case SerializeLoadType.all_global: {
                globalFun();
            }
            case SerializeLoadType.all_archive: {
                const maxArchiveNum = Configer.getConfig(ConfigKey.maxArchiveNum);
                for (let i = 0; i < maxArchiveNum; i++) {
                    const tempIndex = i + 1;
                    archiveFun(tempIndex);
                }
                break;
            }
        }
    }
}